package annotation

import (
	"go/ast"
)

// SingleFileEntryVisitor 这部分和课堂演示差不多，但是我建议你们自己试着写一些
type SingleFileEntryVisitor struct {
	file *fileVisitor
}

func (s *SingleFileEntryVisitor) Get() File {
	if s == nil {
		return File{}
	}
	return s.file.Get()
	// panic("implement me")
}

func (s *SingleFileEntryVisitor) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.File:
		// if n.Doc == nil || len(n.Doc.List) == 0 {
		// 	return s
		// }
		ans := newAnnotations(node.(*ast.File), n.Doc)

		s.file = &fileVisitor{
			ans:     ans,
			visited: true,
		}
		return s.file
	default:
		return s
	}
	// panic("implement me")
}

type fileVisitor struct {
	ans     Annotations[*ast.File]
	types   []*typeVisitor
	visited bool
}

func (f *fileVisitor) Get() File {
	if f == nil {
		return File{}
	}

	if len(f.types) == 0 {
		return File{
			Annotations: f.ans,
			Types:       nil,
		}
	}

	t := make([]Type, 0, len(f.types))
	for _, tv := range f.types {
		t = append(t, Type{
			Annotations: tv.ans,
			Fields:      tv.fields,
		})
	}

	return File{
		Annotations: f.ans,
		Types:       t,
	}
	// panic("implement me")
}

func (f *fileVisitor) Visit(node ast.Node) ast.Visitor {
	if !f.visited {
		return f
	}
	switch n := node.(type) {
	case *ast.TypeSpec:
		if n.Doc == nil {
			return f
		}
		tv := &typeVisitor{
			ans: newAnnotations(node.(*ast.TypeSpec), n.Doc),
		}
		f.types = append(f.types, tv)
		return tv
	default:
		return f
	}
	// panic("implement me")
}

type File struct {
	Annotations[*ast.File]
	Types []Type
}

func (f *File) Visit(node ast.Node) (w ast.Visitor) {
	return f
}

type typeVisitor struct {
	ans    Annotations[*ast.TypeSpec]
	fields []Field
}

func (t *typeVisitor) Get() Type {
	if t == nil {
		return Type{}
	}
	return Type{
		Annotations: t.ans,
		Fields:      t.fields,
	}
	// panic("implement me")
}

func (t *typeVisitor) Visit(node ast.Node) (w ast.Visitor) {
	switch n := node.(type) {
	case *ast.Field:
		if n.Doc == nil {
			return t
		}
		fd := Field{
			Annotations: newAnnotations(node.(*ast.Field), n.Doc),
		}
		t.fields = append(t.fields, fd)
		return nil
	default:
		return t
	}
	// panic("implement me")
}

type Type struct {
	Annotations[*ast.TypeSpec]
	Fields []Field
}

type Field struct {
	Annotations[*ast.Field]
}
